/************************************************************
* @file gkt_malloc.c
************************************************************/
#include <string.h>

#include "gkt_debug.h"
#include "gkt_board.h"
#include "gkt_malloc.h"

#define MALLOC_ALIGNMENT	8
#define MALLOC_ALIGNMASK	(MALLOC_ALIGNMENT - 1)
#define memory_alloc_size_align(size)	\
	(((size) + MALLOC_ALIGNMASK) & (~MALLOC_ALIGNMASK))

#if GKT_MALLOC_FREE_RECORD_ENABLE
#define MALLOC_FREE_ADDR_LO_BITS	20
#define MALLOC_FREE_ADDR_LO_MASK	((1U << MALLOC_FREE_ADDR_LO_BITS) - 1)

#define MALLOC_FREE_SEQ_BITS	(32 - MALLOC_FREE_ADDR_LO_BITS)
#define MALLOC_FREE_SEQ_MAX		((1U << MALLOC_FREE_SEQ_BITS) - 1)
#define MALLOC_FREE_SEQ_MASK	MALLOC_FREE_SEQ_MAX

#ifdef GKT_CONFIG_MALLOC_FREE_RECORD_NUMS
#define MALLOC_FREE_RECORD_NUMS	GKT_CONFIG_MALLOC_FREE_RECORD_NUMS
#if MALLOC_FREE_SEQ_MAX <= (MALLOC_FREE_RECORD_NUMS << 1) 
#error "RECORD NUMS OVERFLOW!"
#endif
#else
#define MALLOC_FREE_RECORD_NUMS	1024
#endif

typedef union _malloc_free_record {
	struct {
		uint32_t	addr_lo : MALLOC_FREE_ADDR_LO_BITS;
		uint32_t	alloc_seq : MALLOC_FREE_SEQ_BITS;
		uint32_t	block_size : MALLOC_FREE_ADDR_LO_BITS;
		uint32_t	free_seq : MALLOC_FREE_SEQ_BITS;
	};
	uint32_t	attrs[2];
} malloc_free_record_s;

typedef struct _malloc_free {
	uint32_t	seq;
	uint32_t	nums;
	uint32_t	block_peak_size;
	uint32_t	total_size;
	uint32_t	peak_size;
	malloc_free_record_s	records[MALLOC_FREE_RECORD_NUMS];
} malloc_free_s;
static malloc_free_s s_memory_af;

static void malloc_free_record(void *block_ptr, uint32_t aligned_size)
{
	uint32_t index, addr_lo;

	addr_lo = (uint32_t)block_ptr & MALLOC_FREE_ADDR_LO_MASK;
	if (aligned_size) {	/* alloc */
		if (s_memory_af.nums < MALLOC_FREE_RECORD_NUMS) {
			s_memory_af.records[s_memory_af.nums].addr_lo = addr_lo;
			s_memory_af.records[s_memory_af.nums].alloc_seq = s_memory_af.seq;
			s_memory_af.records[s_memory_af.nums].block_size = aligned_size;
			s_memory_af.records[s_memory_af.nums].free_seq = MALLOC_FREE_SEQ_MAX;
			s_memory_af.nums++;

			if (s_memory_af.block_peak_size < aligned_size)
				s_memory_af.block_peak_size = aligned_size;

			s_memory_af.total_size += aligned_size;
			if (s_memory_af.peak_size < s_memory_af.total_size)
				s_memory_af.peak_size = s_memory_af.total_size;

			s_memory_af.seq++;
			if (MALLOC_FREE_SEQ_MAX == s_memory_af.seq)
				s_memory_af.seq = 0;
		}
	}
	else {	/* free */
		for (index = 0; index < s_memory_af.nums; index++) {
			if (s_memory_af.records[index].addr_lo == addr_lo) {
				if (MALLOC_FREE_SEQ_MAX == s_memory_af.records[index].free_seq) {
					s_memory_af.records[index].free_seq = s_memory_af.seq;
					s_memory_af.total_size -= s_memory_af.records[index].block_size;

					s_memory_af.seq++;
					if (MALLOC_FREE_SEQ_MAX == s_memory_af.seq)
						s_memory_af.seq = 0;

					break;
				}
			}
		}

		GKT_ASSERT(index < s_memory_af.nums);
	}
	
}

void gkt_malloc_free_show_record(const char *location)
{
	uint32_t index, used_top_index;
	char f_ch;

	if (location)
		gkt_info("MEM AFR: %s\n", location);
	gkt_info("MEM AFR: block(%u - 0x%x), total(0x%x - 0x%x)\n", 
					s_memory_af.nums, s_memory_af.block_peak_size,
					s_memory_af.total_size, s_memory_af.peak_size);

	used_top_index = 0;
	for (index = 0; index < s_memory_af.nums; index++) {
		if (s_memory_af.records[index].free_seq < MALLOC_FREE_SEQ_MAX)	/* freed */
			f_ch = 'F';
		else {	/* not freed */
			f_ch = 'U';
			if (used_top_index < index) {
				s_memory_af.records[used_top_index].attrs[0] = s_memory_af.records[index].attrs[0];
				s_memory_af.records[used_top_index].attrs[1] = s_memory_af.records[index].attrs[1];
			}
			used_top_index++;
		}

		gkt_info(">> (%04u) [%c] 0x%08x - 0x%06x - %u - %u\n", 
						index, f_ch,
						(uint32_t)s_memory_af.records[index].addr_lo | GKT_SRAM_BASEADDR,
						s_memory_af.records[index].block_size,
						s_memory_af.records[index].alloc_seq, s_memory_af.records[index].free_seq);
	}
	s_memory_af.nums = used_top_index;
}
#else
#define malloc_free_record(block_ptr, aligned_size)	do {} while(0)
#endif

#if !defined(GKT_CONFIG_STDLIB_MALLOC) || !GKT_CONFIG_STDLIB_MALLOC

#if defined(GKT_CONFIG_OS_ENABLE) && GKT_CONFIG_OS_ENABLE \
	&& defined(GKT_CONFIG_OS_FREERTOS) && GKT_CONFIG_OS_FREERTOS
#include "FreeRTOS.h"

void *malloc(size_t block_size)
{
	size_t aligned_size;
	void *block_ptr;

	GKT_ASSERT(block_size);
	aligned_size = memory_alloc_size_align(block_size);
	block_ptr = pvPortMalloc(aligned_size);
	if (block_ptr)
		malloc_free_record(block_ptr, aligned_size);

	return block_ptr;
}

void *calloc(size_t n, size_t size)
{
	size_t aligned_size;
	void *block_ptr;

	GKT_ASSERT(n && size);
	aligned_size = n * size;
	aligned_size = memory_alloc_size_align(aligned_size);

	block_ptr = pvPortMalloc(aligned_size);
	if (block_ptr) {
		memset(block_ptr, 0, aligned_size);
		malloc_free_record(block_ptr, aligned_size);
	}

	return block_ptr;
}

void *realloc(void *ptr, size_t size)
{
	size_t aligned_size;
	void *block_ptr;

	GKT_ASSERT(ptr);
	malloc_free_record(ptr, 0);
	vPortFree(ptr);

	aligned_size = memory_alloc_size_align(size);
	block_ptr = pvPortMalloc(aligned_size);
	if (block_ptr)
		malloc_free_record(block_ptr, aligned_size);

	return block_ptr;
}

void free(void *block_ptr)
{
	GKT_ASSERT(block_ptr);
	malloc_free_record(block_ptr, 0);
	vPortFree(block_ptr);
}
#else
/* not implement */
void *malloc(size_t block_size)
{
	GKT_ASSERT(0);
	return NULL;
}

void *calloc(size_t n, size_t size)
{
	GKT_ASSERT(0);
	return NULL;
}

void *realloc(void *ptr, size_t size)
{
	GKT_ASSERT(0);
	return NULL;
}

void free(void *block_ptr)
{
	GKT_ASSERT(0);
}
#endif

#endif	/* !GKT_CONFIG_STDLIB_MALLOC */

